package com.link2room.aeriabots.facade.impl;

import com.link2room.aeriabots.constant.WXConstant;
import com.link2room.aeriabots.dto.param.base.WxComponentParamDto;
import com.link2room.aeriabots.dto.result.base.WXOfficialAccountInfoDto;
import com.link2room.aeriabots.facade.IWXComponentFacadeService;
import com.link2room.aeriabots.service.IWxComponentService;
import com.link2room.aeriabots.thread.task.WxUserInfoSaveTask;
import com.link2room.aeriabots.thread.util.ThreadUtil;
import com.link2room.common.util.L2RAssert;
import com.link2room.common.constant.DefaultValue;
import com.link2room.common.dto.param.base.BaseParamDto;
import com.link2room.common.enums.BaseSysExceptionEnum;
import com.link2room.common.exception.SysException;
import com.link2room.common.jpa.result.QueryResult;
import com.link2room.common.service.ICommonService;
import com.link2room.common.util.DateUtil;
import com.link2room.common.util.StringUtil;
import com.link2room.aeriabots.entity.WxCertificate;
import com.link2room.aeriabots.entity.WxComponent;
import com.link2room.enums.exception.AeriabotsSysExceptionEnum;
import com.tencent.client.ComponentClient;
import com.tencent.common.ComponentConfigure;
import com.tencent.common.Configure;
import com.tencent.common.Log;
import com.tencent.protocol.component.api_component_token_protocol.ApiComponentTokenReqData;
import com.tencent.protocol.component.api_component_token_protocol.ApiComponentTokenResData;
import com.tencent.protocol.component.component_authorizer_protocol.ConAccessTokenResData;
import com.tencent.protocol.component.component_verify_ticket_protocol.ComponentVerifyTicketResData;
import com.tencent.protocol.component.pre_auth_code_protocol.PreAuthCodeReqData;
import com.tencent.protocol.component.pre_auth_code_protocol.PreAuthCodeResData;
import com.tencent.protocol.component.query_auth_protocol.QueryAuthReqData;
import com.tencent.protocol.component.query_auth_protocol.QueryAuthResData;
import com.tencent.protocol.component.refresh_query_auth_protocol.RefreshQueryAuthResData;
import com.tencent.service.component.PreAuthCodeService;
import com.tencent.service.component.QueryAuthService;
import com.tencent.service.component.TicketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by LN on 2016/9/1.
 */
@Component("wxComponentFacadeService")
public class WXComponentFacadeServiceIpml extends BaseFacade implements IWXComponentFacadeService {
    private static Logger logger = LoggerFactory.getLogger(WXComponentFacadeServiceIpml.class);
    @Resource
    private IWxComponentService wxComponentService;
    @Resource
    private ICommonService commonService;
    @Override
    public void setVerifyTicket(WxComponentParamDto verifyTicketDto) throws Exception {
        String msgSignature = verifyTicketDto.getMsgSignature();
        String nonce =verifyTicketDto.getNonce();
        String timeStamp =verifyTicketDto.getTimeStamp();
        String body = verifyTicketDto.getBody();
        WxComponent wct = wxComponentService.findByItem(null, null, "wx_component_token");
        WxComponent wce = wxComponentService.findByItem(null, null, "wx_component_encodingAesKey");
        WxComponent wca = wxComponentService.findByItem(null, null, "wx_component_appid");
        String componentToken = "";
        if(wct != null){
            componentToken = wct.getValue();
        }else {
            throw new SysException(AeriabotsSysExceptionEnum.NO_COMPONENT_TOKEN);
        }
        String encodingAesKey = "";
        if(wce != null){
            encodingAesKey = wce.getValue();
        }else {
            throw new SysException(AeriabotsSysExceptionEnum.NO_ENCODING_AES_KEY);
        }
        String componentAppid = "";
        if(wca != null){
            componentAppid = wca.getValue();
        }else {
            throw new SysException(AeriabotsSysExceptionEnum.NO_COMPONENT_APPID);
        }
        TicketService service = new TicketService(componentAppid,encodingAesKey,componentToken);
        ComponentVerifyTicketResData res = service.requestData(msgSignature, timeStamp, nonce, body);
        if(res != null){
            String ticket = res.getComponentVerifyTicket();
            WXConstant.Component.setVerifyTicket(ticket);
            logger.info("VerifyTicket:"+ticket);
            logger.debug(Log.LOG_TYPE_DEBUG,"VerifyTicket:"+ticket);
        }
    }
    @Override
    public void updateComponentAccessToken() throws Exception {
        WxComponent wat = wxComponentService.findByItem(null, null, WXConstant.WxCertificate.COMPONENT_ACCESS_TOKEN);
        if(wat == null){
            throw new SysException(AeriabotsSysExceptionEnum.NO_COMPONENT_ACCESS_TOKEN);
        }
        Date modTime = wat.getModifyDatetime();
        //AccessToken 获取110分钟以内，则不用更新直接返回数据库中的值，否则需要重新获取access token，并更新数据库中的值
        if(DateUtil.addMinutes(modTime,100).after(new Date())){
            return;
        }else{
            String ticket = WXConstant.Component.getVerifyTicket();
            if(StringUtil.isBlank(ticket)){
                throw new SysException(BaseSysExceptionEnum.DATA_IS_NOT_EXIST.getCode(), BaseSysExceptionEnum.DATA_IS_NOT_EXIST.getMsg()+" No Ticket!Please wait for get ticket ,about 10min.", BaseSysExceptionEnum.DATA_IS_NOT_EXIST.getBizType());
            }
            ComponentClient client = new ComponentClient(new Configure());
            ApiComponentTokenReqData req = new ApiComponentTokenReqData();
            req.setComponent_verify_ticket(ticket);
            WxComponent wx_component_appid = wxComponentService.findByItem(null, null, WXConstant.WxCertificate.COMPONENT_APPID);
            WxComponent wx_component_appsecret = wxComponentService.findByItem(null, null, WXConstant.WxCertificate.COMPONENT_APPID_SECRET);
            req.setComponent_appid(wx_component_appid.getValue());
            req.setComponent_appsecret(wx_component_appsecret.getValue());
            ApiComponentTokenResData res = client.request(req);
            if(StringUtil.isBlank(res.getComponent_access_token())){
                throw new SysException(AeriabotsSysExceptionEnum.WX_SDK_ERROR.getCode(), AeriabotsSysExceptionEnum.WX_SDK_ERROR.getMsg()+" "+res.getErrcode()+":"+res.getErrmsg(),  AeriabotsSysExceptionEnum.WX_SDK_ERROR.getBizType());
            }
            String token = res.getComponent_access_token();
            logger.info("ComponentToken:"+token);
            logger.debug(Log.LOG_TYPE_DEBUG,"ComponentToken:"+token);
            wat.setValue(token);
            commonService.merge(wat);
        }
    }

    @Override
    public void refreshAuthorizerToken() throws Exception {
        WxCertificate wc = new WxCertificate();
        wc.setIsHalt("F");
        QueryResult<WxCertificate> result = commonService.findAllEQ(WxCertificate.class, wc);
        if(result.getResultlist() != null && result.getResultlist().size()>0){
            Date now = new Date();
            List<WxCertificate> wcToUpdate = new ArrayList<>();
            List<WxCertificate> wcList = result.getResultlist();
            String comAccessToken = this.getComponentAccessToken();
            String comAppid = this.getComponentAppid();
            for(WxCertificate wx : wcList){

                if(wx.getRefreshDate() == null){
                    continue;
                }
                //100分钟内不需要更新
                if(DateUtil.addMinutes(wx.getRefreshDate(),100L).after(now)){
                    continue;
                }
                String refreshToken = wx.getRefreshToken();
                //refreshToken为null时说明还未完成授权，continue
                if( StringUtil.isBlank(refreshToken)){
                    continue;
                }
                String appid = wx.getAppid();
                RefreshQueryAuthResData res = ComponentClient.refreshAuthorizerToken(appid, comAppid, refreshToken, comAccessToken);
                if(StringUtil.isNotEmpty(res.getAuthorizer_access_token())){
                    wx.setAccessToken(res.getAuthorizer_access_token());
                    wx.setRefreshDate(now);
                    wx.setRefreshToken(res.getAuthorizer_refresh_token());
                    //请求获取微信jspTicket
                    String jspTicket = "";
                    try {
                        jspTicket = this.requsetWxJspTicket(res.getAuthorizer_access_token());
                    }catch (Exception e){
                        logger.error(e.getMessage());
                    }
                    logger.info("微信jspticket:"+jspTicket);
                    wx.setJspTicket(jspTicket);
                    wcToUpdate.add(wx);
                }
            }
            if(!wcToUpdate.isEmpty()){
                commonService.saveOrMergeList(wcToUpdate);
            }
        }
    }

    @Override
    public String getOfficialAccountsAuthorizerURL(WxComponentParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode,"[lhotelGroupCode] is null.");

        String lhotelCode = paramDto.getLhotelCode();
        L2RAssert.stringBlank(lhotelCode,"[lhotelCode] is null.");
        //保存appID
        WxCertificate wc = new WxCertificate();
        wc.setAppid(paramDto.getAppId());
        WxCertificate res = commonService.findOneEQ(WxCertificate.class, wc);
        //若没有则保存，有则更新
        if(res != null){
            if(!lhotelGroupCode.equals(res.getLhotelGroupCode()) || !lhotelCode.equals(res.getLhotelCode())){
                throw new SysException(AeriabotsSysExceptionEnum.OFFICEACCOUNT_REGIESTER_ERROR,"[lhotelGroupCode] "+lhotelGroupCode+"[lhotelCode]"+lhotelCode+" 重复.");
            }
            res.setAppid(paramDto.getAppId());
            res.setWxMchId(paramDto.getMchId());
            commonService.merge(res);
        }else{
            wc.setLhotelGroupCode(paramDto.getLhotelGroupCode());
            wc.setLhotelCode(paramDto.getLhotelCode());
            wc.setAppid(paramDto.getAppId());
            wc.setWxMchId(paramDto.getMchId());
            commonService.save(wc);
        }
        //组装URL
        String redirectUrl = this.getRedirect_uriAuthorizerURL();
        String component_appid = this.getComponentAppid();
        String preAuthCode = this.getPreAuthCode(component_appid);
        String url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid="+component_appid+"&"
                +"pre_auth_code="+preAuthCode+"&redirect_uri="+redirectUrl;
        return url;
    }

    @Override
    public String getWxComponentAppid() throws Exception {
        return getComponentAppid();
    }

    @Override
    public void saveAuthorizerAccessToken(WxComponentParamDto paramDto) throws Exception {
        String comAppId = this.getComponentAppid();
        String com_ac_token = this.getComponentAccessToken();
        QueryAuthReqData req = new QueryAuthReqData();
        req.setComponent_appid(comAppId);
        req.setAuthorization_code(paramDto.getAuthCode());
        QueryAuthResData res = new QueryAuthService(com_ac_token, new ComponentConfigure()).sendRequestReturnObj(req);
        if(res.getAuthorization_info() == null){
            throw new SysException(AeriabotsSysExceptionEnum.OFFICEACCOUNT_REGIESTER_ERROR,"res.getErrmsg()+\" \"+res.getErrcode()");
        }
        String authorizerAccessToken = res.getAuthorization_info().getAuthorizer_access_token();
        String authorizerAppid = res.getAuthorization_info().getAuthorizer_appid();
        String authorizerRefreshToken = res.getAuthorization_info().getAuthorizer_refresh_token();
        String hotelGroupCode = "";
        String hotelCode = "";
        //根据appid查出对应酒店code
        WxCertificate wc = new WxCertificate();
        wc.setAppid(authorizerAppid);

        //查询是否已经授权，若有更新否则保存
        WxCertificate wcRes = commonService.findOneEQ(WxCertificate.class, wc);
        if(wcRes==null){
            hotelGroupCode = authorizerAppid;
            hotelCode = authorizerAppid;
            wcRes = new WxCertificate();
            wcRes.setLhotelGroupCode(hotelGroupCode);
            wcRes.setLhotelCode(hotelCode);
            wcRes.setAccessToken(authorizerAccessToken);
            wcRes.setRefreshToken(authorizerRefreshToken);
            wcRes.setRefreshDate(new Date());
        }else {
            wcRes.setAccessToken(authorizerAccessToken);
            wcRes.setRefreshToken(authorizerRefreshToken);
            wcRes.setRefreshDate(new Date());
        }
       this.saveOrUpdaateAuthorization(wcRes);
    }

    @Override
    public String getAccessCodeURL(WxComponentParamDto paramDto) throws Exception {
        String componentAppid = this.getComponentAppid();

        WxCertificate wc = this.getWxCertificate(paramDto.getLhotelGroupCode(),paramDto.getLhotelCode());

        String redirectURL = URLEncoder.encode(paramDto.getRedirectURL(), "UTF-8");
        return  this.getWxWebAuthorizeURL(wc.getAppid(),redirectURL,componentAppid,paramDto.getScope());
    }

    @Override
    public String getOpenid(WxComponentParamDto paramDto) throws Exception {
        WxCertificate wx = this.getWxCertificate(paramDto.getLhotelGroupCode(), paramDto.getLhotelCode());
        Configure configuer = new Configure();
        configuer.setAppID(wx.getAppid());
        configuer.setComponentAppid(this.getComponentAppid());
        ComponentClient client = new ComponentClient(configuer);
        ConAccessTokenResData s = client.reqestConAccessTokenResData(paramDto.getAccessCode(), this.getComponentAccessToken());
        if(StringUtil.isBlank(s.getOpenid())){
            throw new SysException(AeriabotsSysExceptionEnum.WX_SDK_ERROR.getCode(), AeriabotsSysExceptionEnum.WX_SDK_ERROR.getMsg()+s.getErrmsg(), AeriabotsSysExceptionEnum.WX_SDK_ERROR.getBizType());
        }
        //异步保存openid
        ThreadUtil.getExecutor().submit(new WxUserInfoSaveTask(paramDto.getLhotelGroupCode(),paramDto.getLhotelCode(),s.getScope(),s.getOpenid(),s.getAccess_token()));
        return s.getOpenid();
    }

    @Override
    public String getAuthorizerAccessToken(WxComponentParamDto paramDto) throws Exception {
        WxCertificate wc = this.getWxCertificate(paramDto.getLhotelGroupCode(),paramDto.getLhotelCode());
        return wc.getAccessToken();
    }

    /**
     * 微信公众好基本信息
     *
     * @param paramDto
     * @return
     * @throws Exception
     */
    @Override
    public WXOfficialAccountInfoDto getWXOfficialAccountInfoDto(BaseParamDto paramDto) throws Exception {
        WxCertificate wc = getWxCertificate(paramDto.getLhotelGroupCode(), null);
        WXOfficialAccountInfoDto res = new WXOfficialAccountInfoDto();
        res.setAppid(wc.getAppid());
        res.setMchId(wc.getWxMchId());

        return res;
    }


    private String getWxWebAuthorizeURL(String appid, String redirectURL, String componentAppid, String scope) throws Exception {
        String URL = "";
        if(StringUtil.isEmpty(scope)){
            URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+
                    "&redirect_uri="+redirectURL+"&response_type=code&scope=snsapi_base&state=asd&component_appid="+componentAppid
                    +"#wechat_redirect";

        }else{
            URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+
                    "&redirect_uri="+  redirectURL+"&response_type=code&scope="+scope+"&state=asd&component_appid="+componentAppid
                    +"#wechat_redirect";
        }
        logger.info(Log.LOG_TYPE_INFO,"getWxWebAuthorizeURL:"+URL);
        return URL;
    }

    private void saveOrUpdaateAuthorization(WxCertificate wcRes ) throws Exception {
        List<WxCertificate> wcList = new ArrayList<>();
        wcList.add(wcRes);
        commonService.saveOrMergeList(wcList);
    }

    private String getRedirect_uriAuthorizerURL() throws Exception {
        WxComponent wc = wxComponentService.findByItem(null, null, WXConstant.WxCertificate.COMPONENT_REIDRECT_URL);
        String resouce = "";
        if(wc != null){
            resouce =  wc.getValue();
        } else {
            throw new SysException(BaseSysExceptionEnum.DATA_IS_NOT_EXIST.getCode(), BaseSysExceptionEnum.DATA_IS_NOT_EXIST.getMsg()+" 检查wx_component :item = "+WXConstant.WxCertificate.COMPONENT_REIDRECT_URL, BaseSysExceptionEnum.DATA_IS_NOT_EXIST.getBizType());
        }
        return resouce;
    }


    private String getPreAuthCode(String componentAppId) throws Exception {
        String com_ac_token = this.getComponentAccessToken();
        PreAuthCodeService pcs = new PreAuthCodeService(com_ac_token,new ComponentConfigure());
        PreAuthCodeReqData req = new PreAuthCodeReqData();
        wxComponentService.findByItem(DefaultValue.LHOTEL_GROUP_CODE, DefaultValue.LHOTEL_CODE, WXConstant.WxCertificate.COMPONENT_APPID);
        req.setComponent_appid(componentAppId);
        PreAuthCodeResData res = pcs.sendRequestReturnObj(req);
        return res.getPre_auth_code();
    }

}
